package io;

import java.util.ArrayList;
import java.util.Stack;
import java.util.logging.Logger;
import light.Light;
import org.xml.sax.*;

import camera.Camera;

import primitives.AbstractShape;
import primitives.Plane;
import primitives.Sphere;
import primitives.Triangle;
import primitives.World3D;
import surface.Surface;
import colour.Colour;
import vector.Vector;

/**
 * This class gets contents from a XML file through overwriting some methods
 * such as endDocument() endElement()
 * 
 */
public class ReadXML implements ContentHandler {
	
	Logger log = Logger.getLogger("Tracer");

	private ArrayList<AbstractShape> abstractShape;
	private Sphere sphere;
	private Surface surface;
	private Triangle triangle;
	private int triangleType = 1;
	private Plane plane;

	private Camera camareAll;
	ArrayList<Light> arrayLightAll;

	private Vector pCameraLocation;
	private Vector pCameraSky;
	private Vector vCameraLook_at;

	private Colour cBackground;

	private Vector pSphereLocation;
	private Vector vectorSpherePole;
	private Vector vectorSphereEquator;

	private Vector pTriangleC0;
	private Vector pTriangleC1;
	private Vector pTriangleC2;
	private Vector vTriangleV1;
	private Vector vTriangleV2;

	private Vector vPlaneNormal;

	private Vector pPlanePoint;

	private Vector pLightPosition;

	private Colour colourPrimitive;

	private double radius;
	private double distance;

	private boolean bCamera = false;
	private boolean bLocation = false;
	private boolean bSky = false;
	private boolean bLook_at = false;

	private boolean bWorld = false;

	private boolean bLight = false;

	private boolean bSphere = false;
	private boolean bPole = false;
	private boolean bEquator = false;
	private boolean bPigment = false;

	private boolean bTriangle = false;
	private boolean bC0 = false;
	private boolean bC1 = false;
	private boolean bC2 = false;
	private boolean bV1 = false;
	private boolean bV2 = false;

	private boolean bBackground = false;

	private boolean bPlane = false;
	private boolean bNormal = false;
	private boolean bPoint = false;
	private Vector vector;
	Stack<Vector> vectorStack = new Stack<Vector>();
	private Colour colour;
	Stack<Colour> colorStack = new Stack<Colour>();

	private String shareDiffuse = "1";
	private String shareReflect = "0";

	Stack<String> finishDiffuse = new Stack<String>();
	Stack<String> finishReflect = new Stack<String>();

	private String imageLocationString;
	Stack<String> imageStack = new Stack<String>();

	public World3D getWorld3D() {
		return new World3D(abstractShape, arrayLightAll, cBackground, camareAll);

	}

	public void characters(char[] arg0, int arg1, int arg2) throws SAXException {

	}
	public void endDocument() throws SAXException {
	}

	/**
	 * Receive notification of the end of an element.
	 * 
	 * @param arg0
	 *            the Namespace URI, or the empty string if the element has no
	 *            Namespace URI or if Namespace processing is not being
	 *            performed
	 * @param arg1
	 *            the local name (without prefix), or the empty string if
	 *            Namespace processing is not being performed
	 * @param arg2
	 *            the qualified XML name (with prefix), or the empty string if
	 *            qualified names are not available
	 * @throws org.xml.sax.SAXException
	 *             any SAX exception, possibly wrapping another exception
	 */
	public void endElement(String arg0, String arg1, String arg2) throws SAXException {
		if (arg2.equals("vector")) {
			if (this.bCamera == true) {
				if (this.bLocation == true) {
					this.pCameraLocation = (Vector) vectorStack.pop();
				}
				if (this.bSky == true) {
					this.pCameraSky = (Vector) vectorStack.pop();
				}
				if (this.bLook_at == true) {
					this.vCameraLook_at = (Vector) vectorStack.pop();
				}
				camareAll = new Camera(pCameraLocation, vCameraLook_at, pCameraSky);
			}
			if (this.bWorld == true) {
				if (this.bSphere == true) {
					if (this.bLocation == true) {
						this.pSphereLocation = (Vector) vectorStack.pop();
					}
					if (this.bPole == true) {
						this.vectorSpherePole = (Vector) vectorStack.pop();
					}
					if (this.bEquator == true) {
						this.vectorSphereEquator = (Vector) vectorStack.pop();
					}
				}
				if (this.bTriangle == true) {
					if (this.bC0 == true) {
						this.pTriangleC0 = (Vector) vectorStack.pop();
					}
					if (this.bC1 == true) {
						this.pTriangleC1 = (Vector) vectorStack.pop();
					}
					if (this.bC2 == true) {
						this.pTriangleC2 = (Vector) vectorStack.pop();
					}
					if (this.bV1 == true) {
						this.vTriangleV1 = (Vector) vectorStack.pop();
					}
					if (this.bV2 == true) {
						this.vTriangleV2 = (Vector) vectorStack.pop();
					}
				}
				if (this.bPlane == true) {
					if (this.bNormal == true) {
						this.vPlaneNormal = (Vector) vectorStack.pop();
					}
					if (this.bPoint == true) {
						this.pPlanePoint = (Vector) vectorStack.pop();
					} else if (this.bPoint == false) {
					}
				}
				if (this.bLight == true) {
					this.pLightPosition = (Vector) vectorStack.pop();
					Light temp = new Light(this.pLightPosition);
					this.arrayLightAll.add(temp);
				}
			}
		}
		if (arg2.equals("color")) {
			if (this.bWorld == true) {
				if (this.bPigment == true) {
					if (this.bSphere == true) {
						colourPrimitive = (Colour) colorStack.pop();
					}
					if (this.bTriangle == true) {
						colourPrimitive = (Colour) colorStack.pop();
					}
					if (this.bPlane == true) {
						colourPrimitive = (Colour) colorStack.pop();
					}
				}
			}
			if (this.bBackground == true) {
				cBackground = ((Colour) colorStack.pop());
			}
		}
		if (arg2.equals("location")) {
			this.bLocation = false;
		}
		if (arg2.equals("camera")) {
			this.bCamera = false;
		}
		if (arg2.equals("sky")) {
			this.bSky = false;
		}
		if (arg2.equals("look_at")) {
			this.bLook_at = false;
		}
		if (arg2.equals("world")) {
			this.bWorld = false;
		}
		if (arg2.equals("sphere")) {
			this.bSphere = false;
			sphere = new Sphere(pSphereLocation, radius, vectorSpherePole, vectorSphereEquator, surface);
			abstractShape.add((AbstractShape)sphere);
		}
		if (arg2.equals("pole")) {
			this.bPole = false;
		}
		if (arg2.equals("equator")) {
			this.bEquator = false;
		}
		if (arg2.equals("surface")) {
			double tempDiffuse = finishDiffuse.empty() ? (Double.parseDouble(shareDiffuse)) : (Double
					.parseDouble(finishDiffuse.pop()));
			double tempReflect = finishReflect.empty() ? (Double.parseDouble(shareReflect)) : (Double
					.parseDouble(finishReflect.pop()));
			if (colourPrimitive == null) {
				surface = new Surface(tempDiffuse, tempReflect, imageLocationString);
				imageLocationString = null;
			} else {
				surface = new Surface(tempDiffuse, tempReflect, colourPrimitive);
				colourPrimitive = null;
			}
		}
		if (arg2.equals("pigment")) {
			this.bPigment = false;
		}
		if (arg2.equals("light")) {
			this.bLight = false;
		}
		if (arg2.equals("triangle")) {
			this.bTriangle = false;
			if (pTriangleC1 != null) {
				triangleType = 1;
				triangle = new Triangle(pTriangleC0, pTriangleC1, pTriangleC2, surface, triangleType);
				abstractShape.add(triangle);
			} else {
				triangleType = 0;
				triangle = new Triangle(pTriangleC0, vTriangleV1, vTriangleV2, surface, triangleType);
				abstractShape.add(triangle);
			}

		}
		if (arg2.equals("c0")) {
			this.bC0 = false;
		}
		if (arg2.equals("c1")) {
			this.bC1 = false;
		}
		if (arg2.equals("c2")) {
			this.bC2 = false;
		}
		if (arg2.equals("v1")) {
			this.bV1 = false;
		}
		if (arg2.equals("v2")) {
			this.bV2 = false;
		}
		if (arg2.equals("plane")) {
			this.bPlane = false;
			if (pPlanePoint != null) {
				plane = new Plane(vPlaneNormal, pPlanePoint, surface);
				abstractShape.add(plane);
			} else {
				plane = new Plane(vPlaneNormal, distance, surface);
				abstractShape.add(plane);
			}
		}
		if (arg2.equals("normal")) {
			this.bNormal = false;
		}
		if (arg2.equals("point")) {
			this.bPoint = false;
		}
		if (arg2.equals("background")) {
			this.bBackground = false;
		}
	}

	public void endPrefixMapping(String arg0) throws SAXException {

	}

	public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {

	}

	public void processingInstruction(String arg0, String arg1) throws SAXException {

	}

	public void setDocumentLocator(Locator arg0) {

	}

	public void skippedEntity(String arg0) throws SAXException {

	}

	/**
	 * Receive notification of the beginning of a document.
	 * 
	 * <p>
	 * The SAX parser will invoke this method only once, before any other event
	 * callbacks (except for {@link #setDocumentLocator setDocumentLocator}).
	 * </p>
	 * 
	 * @throws org.xml.sax.SAXException
	 *             any SAX exception, possibly wrapping another exception
	 * @see #endDocument
	 */
	public void startDocument() throws SAXException {
		arrayLightAll = new ArrayList<Light>();
		cBackground = null;
		abstractShape = new ArrayList<AbstractShape>();
	}

	public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException {
		if (arg2.equals("camera")) {
			this.bCamera = true;
		}
		if (arg2.equals("location")) {
			this.bLocation = true;
		}
		if (arg2.equals("sky")) {
			this.bSky = true;
		}
		if (arg2.equals("look_at")) {
			this.bLook_at = true;
		}
		if (arg2.equals("world")) {
			this.bWorld = true;
		}
		if (arg2.equals("sphere")) {
			this.bSphere = true;
			radius = Double.parseDouble(arg3.getValue("radius"));
		}
		if (arg2.equals("plane")) {
			this.bPlane = true;
			if (arg3.getValue("distance") != null) {
				distance = Double.parseDouble(arg3.getValue("distance"));
			}
		}
		if (arg2.equals("pole")) {
			this.bPole = true;
		}
		if (arg2.equals("equator")) {
			this.bEquator = true;
		}
		if (arg2.equals("pigment")) {
			this.bPigment = true;
		}
		if (arg2.equals("finish")) {
			if (arg3.getValue("diffuse") != null) {
				this.shareDiffuse = arg3.getValue("diffuse");
				finishDiffuse.push(shareDiffuse);
			}
			if (arg3.getValue("reflect") != null) {
				this.shareReflect = arg3.getValue("reflect");
				this.finishReflect.push(shareReflect);
			}
		}
		if (arg2.equals("background")) {
			this.bBackground = true;
		}
		if (arg2.equals("triangle")) {
			this.bTriangle = true;
		}
		if (arg2.equals("c0")) {
			this.bC0 = true;
		}
		if (arg2.equals("c1")) {
			this.bC1 = true;
		}
		if (arg2.equals("c2")) {
			this.bC2 = true;
		}
		if (arg2.equals("v1")) {
			this.bV1 = true;
		}
		if (arg2.equals("v2")) {
			this.bV2 = true;
		}
		if (arg2.equals("normal")) {
			this.bNormal = true;
		}
		if (arg2.equals("point")) {
			this.bPoint = true;
		}
		if (arg2.equals("light")) {
			this.bLight = true;
		}
		if (arg2.equals("vector")) {
			vector = new Vector(Double.parseDouble(arg3.getValue("x")), Double.parseDouble

			(arg3.getValue("y")), Double.parseDouble(arg3.getValue("z")));
			vectorStack.push(vector);
		}
		if (arg2.equals("color")) {
			int red = (int) (255 * Double.parseDouble(arg3.getValue("red")));
			int blue = (int) (255 * Double.parseDouble(arg3.getValue("blue")));
			int green = (int) (255 * Double.parseDouble(arg3.getValue("green")));
			colour = new Colour(red, green, blue);
			this.colorStack.push(colour);
		}
		if (arg2.equals("ppm")) {
			imageLocationString = arg3.getValue("file");
			this.imageStack.push(this.imageLocationString);
		}
	}

	public void startPrefixMapping(String arg0, String arg1) throws SAXException {

	}

}
